package ace.module.account.core.impl.service.biz.impl;

import ace.cmp.core.constants.CoreConstant;
import ace.cmp.core.utils.SystemClockUtils;
import ace.cmp.util.AceEnumUtils;
import ace.module.account.api.enums.IamAccountEventRecordEventTypeEnum;
import ace.module.account.api.enums.IamAccountProfileRegisterSourceFromEnum;
import ace.module.account.api.enums.IamAccountProfileSexEnum;
import ace.module.account.api.enums.IamAccountStatusEnum;
import ace.module.account.api.model.input.RegisterQuickByPhoneInput;
import ace.module.account.api.model.input.RegisterQuickByUsernameInput;
import ace.module.account.api.model.input.RegisterQuickByWxMiniAppIdAndOpenIdInput;
import ace.module.account.api.model.input.RegisterQuickCommonInput;
import ace.module.account.api.model.input.account.IamAdminAccountAdminSaveInput;
import ace.module.account.core.impl.dao.aggregate.IamAccountAggregate;
import ace.module.account.core.impl.dao.entity.IamAccount;
import ace.module.account.core.impl.dao.entity.IamAccountEventRecord;
import ace.module.account.core.impl.dao.entity.IamAccountPassport;
import ace.module.account.core.impl.dao.entity.IamAccountPassword;
import ace.module.account.core.impl.dao.entity.IamAccountProfile;
import ace.module.account.core.impl.dao.entity.IamApplication;
import ace.module.account.core.impl.dao.repository.IamApplicationRepository;
import ace.module.account.core.impl.manager.IamAccountEventRecordManager;
import ace.module.account.core.impl.manager.IamAccountManager;
import ace.module.account.core.impl.manager.IamAccountPassportManager;
import ace.module.account.core.impl.manager.IamAccountPasswordManager;
import ace.module.account.core.impl.manager.IamAccountProfileManager;
import ace.module.account.core.impl.manager.IamApplicationManager;
import ace.module.account.core.impl.manager.provider.UpmsApplicationProvider;
import ace.module.account.core.impl.manager.provider.UpmsOrganizationProvider;
import ace.module.account.core.impl.manager.provider.UpmsRoleProvider;
import ace.module.account.core.impl.service.biz.IamAccountBiz;
import ace.module.account.core.impl.service.model.IamAccountEventParams;
import ace.module.account.core.impl.service.model.IamAccountRegisterEventParams;
import ace.module.account.core.impl.service.model.input.RegisterCommonInput;
import ace.module.upms.api.model.dto.UpmsApplicationDto;
import jakarta.annotation.Nullable;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author caspar
 * @date 2023/3/20 9:23
 */
@AllArgsConstructor
@Component
public class IamAccountBizImpl implements IamAccountBiz {


  private final IamAccountAggregate iamAccountAggregate;
  private final IamAccountEventRecordManager iamAccountEventRecordManager;
  private final IamAccountPassportManager iamAccountPassportManager;
  private final IamAccountManager iamAccountManager;
  private final IamApplicationManager iamApplicationManager;
  private final IamAccountPasswordManager iamAccountPasswordManager;
  private final IamAccountProfileManager iamAccountProfileManager;
  private final IamApplicationRepository iamApplicationRepository;

  private final UpmsApplicationProvider upmsApplicationProvider;
  private final UpmsOrganizationProvider upmsOrganizationProvider;
  private final UpmsRoleProvider upmsRoleProvider;


  @Override
  public Long registerQuickByPhone(RegisterQuickByPhoneInput input) {
    IamApplication iamApplication = iamApplicationRepository.findAndCheckDataIsNotNullById(input.getCommonInput().getIamAppId());

    // 已经存在手机号码
    this.iamAccountPassportManager.checkExistsPhoneNumber(input.getCommonInput().getIamAppId(), input.getPhone());

    Long iamAccountId = this.iamAccountManager.getNewId();

    Long iamAppId = iamApplication.getId();

    IamAccountRegisterEventParams eventParams = IamAccountRegisterEventParams.builder()
        .iamAppId(iamApplication.getId())
        .accountId(iamAccountId)
        .registerSourceFrom(input.getCommonInput().getRegisterSourceFrom())
        .build();

    IamAccountPassport iamPhoneAccountPassport = this.iamAccountPassportManager.buildPhonePassport(iamAccountId,
        iamAppId,
        input.getPhone()
    );

    this.registerQuickBy(
        iamApplication.getId(),
        iamAccountId,
        input.getCommonInput(),
        iamPhoneAccountPassport,
        eventParams);

    return iamAccountId;
  }

  @Override
  public Long registerQuickByWxMiniAppIdAndOpenId(RegisterQuickByWxMiniAppIdAndOpenIdInput input) {
    IamApplication iamApplication = iamApplicationRepository.findAndCheckDataIsNotNullById(input.getCommonInput().getIamAppId());

    Long iamAppId = iamApplication.getId();

    // 检查已经存在对应openid
    this.iamAccountPassportManager.checkExistsWxMiniAppIdAndOpenId(iamAppId, input.getWxMiniAppId(), input.getOpenId());

    Long iamAccountId = this.iamAccountManager.getNewId();

    IamAccountRegisterEventParams eventParams = IamAccountRegisterEventParams.builder()
        .iamAppId(iamApplication.getId())
        .accountId(iamAccountId)
        .registerSourceFrom(input.getCommonInput().getRegisterSourceFrom())
        .build();

    IamAccountPassport iamWxMiniAppAccountPassport = this.iamAccountPassportManager.buildWxMiniAppPassport(iamAccountId,
        input.getWxMiniAppId(),
        input.getOpenId(),
        input.getUnionId()
    );

    this.registerQuickBy(
        iamAppId,
        iamAccountId,
        input.getCommonInput(),
        iamWxMiniAppAccountPassport,
        eventParams);

    return iamAccountId;
  }

  @Override
  public Long registerQuickByUsername(RegisterQuickByUsernameInput input) {
    IamApplication iamApplication = iamApplicationRepository.findAndCheckDataIsNotNullById(input.getCommonInput().getIamAppId());

    this.iamAccountPassportManager.checkExistsByUserName(input.getCommonInput().getIamAppId(), input.getUsername());

    Long iamAccountId = this.iamAccountManager.getNewId();

    Long iamAppId = iamApplication.getId();

    IamAccountPassport iamUsernameAccountPassport = this.iamAccountPassportManager.buildUsernamePassport(iamAccountId, iamAppId, input.getUsername());

    IamAccountRegisterEventParams eventParams = IamAccountRegisterEventParams.builder()
        .iamAppId(iamAppId)
        .accountId(iamAccountId)
        .registerSourceFrom(input.getCommonInput().getRegisterSourceFrom())
        .build();

    this.registerQuickBy(
        iamAppId,
        iamAccountId,
        input.getCommonInput(),
        iamUsernameAccountPassport,
        eventParams);

    return iamAccountId;
  }


  @Override
  public Long adminAdd(IamAdminAccountAdminSaveInput input) {

    IamApplication iamApplication = this.iamApplicationManager.getAndCheckByNotNullAllAdminIamApplicationAndDeleteFlagIsFalse();
    Long iamAccountId = this.iamAccountManager.getNewId();
    Long iamAppId = iamApplication.getId();

    IamAccountPassport usernamePassport = this.iamAccountPassportManager.buildUsernamePassport(iamAccountId, iamAppId, input.getUsername());
    IamAccountStatusEnum statusEnum = AceEnumUtils.getEnum(IamAccountStatusEnum.class, input.getStatus());
    IamAccountProfileSexEnum sexEnum = AceEnumUtils.getEnum(IamAccountProfileSexEnum.class, input.getSex());
    IamAccountProfileRegisterSourceFromEnum registerSourceFromEnum = AceEnumUtils.getEnum(IamAccountProfileRegisterSourceFromEnum.class, input.getRegisterSourceFrom());

    RegisterCommonInput registerCommonInput = RegisterCommonInput.builder()
        .signature(input.getSignature())
        .realName(input.getRealName())
        .statusEnum(statusEnum)
        .passwordChangeRequired(input.getPasswordChangeRequired())
        .avatar(input.getAvatar())
        .contactInfo(input.getContactInfo())
        .sexEnum(sexEnum)
        .registerSourceFromEnum(registerSourceFromEnum)
        .nickName(input.getNickName())
        .id(iamAccountId)
        .iamAppId(iamAppId)
        .birthday(input.getBirthday())
        .build();

    IamAccountRegisterEventParams eventParams = IamAccountRegisterEventParams.builder()
        .iamAppId(iamApplication.getId())
        .accountId(iamAccountId)
        .registerSourceFrom(IamAccountProfileRegisterSourceFromEnum.ADMIN_SYSTEM.getCode())
        .build();

    this.registerCoreBy(
        registerCommonInput,
        usernamePassport,
        eventParams,
        input.getPassword()
    );

    UpmsApplicationDto upmsAdminApplicationDto = this.upmsApplicationProvider.getAndCheckNotNullAdminApplication();

    this.upmsRoleProvider.saveRoleAccountRelation(upmsAdminApplicationDto.getId(), iamAccountId, input.getRoleIds());

    this.upmsOrganizationProvider.saveOrgAccountRelation(upmsAdminApplicationDto.getId(), iamAccountId, input.getOrgIds());

    return iamAccountId;
  }

  @Transactional(rollbackFor = Throwable.class)
  @Override
  public Long adminModify(IamAdminAccountAdminSaveInput input) {

    IamApplication iamAdminApplication = this.iamApplicationManager.getAndCheckByNotNullAllAdminIamApplicationAndDeleteFlagIsFalse();

    IamAccount iamAccount = this.iamAccountManager.getAndCheckNotNullByIdAndIamAppIdAndDeleteFlagIsFalse(input.getId(), iamAdminApplication.getId());

    this.iamAccountManager.modify(
        iamAccount,
        input.getStatus(),
        input.getPasswordChangeRequired()
    );

    this.iamAccountProfileManager.addOrModify(
        iamAccount,
        input.getNickName(),
        input.getAvatar(),
        input.getRealName(),
        input.getSex(),
        input.getBirthday(),
        input.getSignature(),
        input.getContactInfo(),
        input.getRegisterSourceFrom()
    );

    this.iamAccountPassportManager.addOrModifyByUsername(iamAccount, input.getUsername());

    this.iamAccountPasswordManager.addOrModifyIfPasswordIsNotEmpty(iamAccount, input.getPassword());

    return iamAccount.getId();
  }


  private void registerQuickBy(
      Long iamAppId,
      Long iamAccountId,
      RegisterQuickCommonInput commonInput,
      IamAccountPassport iamAccountPassport,
      IamAccountEventParams eventParams
  ) {

    IamAccountProfileRegisterSourceFromEnum iamAccountProfileRegisterSourceFromEnum = AceEnumUtils.getEnum(
        IamAccountProfileRegisterSourceFromEnum.class,
        commonInput.getRegisterSourceFrom(),
        IamAccountProfileRegisterSourceFromEnum.UNKNOWN
    );

    IamAccountProfileSexEnum sexEnum = AceEnumUtils.getEnum(IamAccountProfileSexEnum.class, commonInput.getSex(), IamAccountProfileSexEnum.UNKNOWN);

    RegisterCommonInput registerCommonInput = RegisterCommonInput.builder()
        .contactInfo(commonInput.getContact())
        .sexEnum(sexEnum)
        .registerSourceFromEnum(iamAccountProfileRegisterSourceFromEnum)
        .avatar(commonInput.getAvatar())
        .birthday(commonInput.getBirthday())
        .iamAppId(iamAppId)
        .id(iamAccountId)
        .statusEnum(IamAccountStatusEnum.NORMAL)
        .nickName(commonInput.getNickName())
        .passwordChangeRequired(commonInput.getPasswordChangeRequired())
        .realName(commonInput.getRealName())
        .signature(commonInput.getSignature())

        .build();

    this.registerCoreBy(
        registerCommonInput,
        iamAccountPassport,
        eventParams,
        commonInput.getPassword()
    );
  }


  private void registerCoreBy(
      RegisterCommonInput registerCommonInput,
      IamAccountPassport iamAccountPassport,
      IamAccountEventParams eventParams,
      @Nullable String password) {

    Long createTime = SystemClockUtils.currentTimeMillis();
    Long iamAccountId = registerCommonInput.getId();
    Long iamAppId = registerCommonInput.getIamAppId();

    IamAccount iamAccount = IamAccount.builder()
        .id(iamAccountId)
        .iamAppId(iamAppId)
        .status(registerCommonInput.getStatusEnum().getCode())
        .passwordChangeRequired(registerCommonInput.getPasswordChangeRequired())
        .deleteFlag(CoreConstant.DELETE_FLAG_NOT)
        .createTime(createTime)
        .updateTime(createTime)
        .rowVersion(CoreConstant.DEFAULT_ROW_VERSION)
        .build();

    IamAccountPassword iamAccountPassword = null;
    if (StringUtils.isNotBlank(password)) {
      iamAccountPassword = this.iamAccountPasswordManager.build(iamAccountId, iamAppId, password);
    }

    IamAccountProfile iamAccountProfile = IamAccountProfile.builder()
        .id(this.iamAccountProfileManager.getNewId())
        .iamAppId(iamAppId)
        .iamAccountId(iamAccount.getId())
        .avatar(registerCommonInput.getAvatar())
        .birthday(registerCommonInput.getBirthday())
        .sex(registerCommonInput.getSexEnum().getCode())
        .realName(registerCommonInput.getRealName())
        .nickName(registerCommonInput.getNickName())
        .signature(registerCommonInput.getSignature())
        .registerSourceFrom(registerCommonInput.getRegisterSourceFromEnum().getCode())
        .birthday(registerCommonInput.getBirthday())

        .deleteFlag(CoreConstant.DELETE_FLAG_NOT)
        .createTime(createTime)
        .updateTime(createTime)
        .rowVersion(CoreConstant.DEFAULT_ROW_VERSION)
        .build();

    IamAccountEventRecord iamAccountEventRecord = iamAccountEventRecordManager
        .from(iamAppId, iamAccountId, IamAccountEventRecordEventTypeEnum.REGISTER, eventParams)
        .build();

    this.iamAccountAggregate.insertRegisterAccount(iamAccount,
        iamAccountPassport,
        iamAccountProfile,
        iamAccountEventRecord,
        iamAccountPassword
    );
  }


}
